home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / timer / timerClock.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-19  |  21.8 KB  |  755 lines

  1. /*
  2.  * timerClock.c --
  3.  *
  4.  *    Kernel utility procedures to manipulate clocks values.
  5.  *
  6.  *  The routines in this module provide the interface between routines in 
  7.  *  the human oriented time and the machine dependent representation of 
  8.  *  time, the Timer_Ticks format. The Timer_Ticks format is used
  9.  *  for a specific purpose: to make the operations associated with the
  10.  *  callback timer and timer queue run fast. These operations include
  11.  *  starting the timer, scheduling a routine and calling a routine at its
  12.  *  scheduled time.  Unlike the Time format, which represents time in
  13.  *  seconds and microseconds, the Timer_Ticks format represents time in a
  14.  *  machine-dependent way. Timer_Ticks are defined in timerTicks.h in the 
  15.  *  machine-dependent directorys. Example Timer_Ticks format are as follows:
  16.  *  Sun-2: Timer_Ticks is a value based on the hardware's free-running 
  17.  *  counter and the number of times it has wrapped around. 
  18.  *  Sun-3: the hardware free-running counter format is easily converted
  19.  *  to the Time format, so no distinction is made between Time and Timer_Ticks.
  20.  *
  21.  *  A time value in the Timer_Ticks format is a hardware-dependent 64-bit
  22.  *  number that represents a specific or absolute point in time since some
  23.  *  some event (on the Sun-2, since the system was booted).  A time value
  24.  *  that is relative to an absolute time is called an interval.  By
  25.  *  definition, an interval is a hardware-dependent unsigned 32-bit number.  
  26.  *  The operations * and / can be used on intervals since they are integers.
  27.  *
  28.  *  There are several constraints imposed on the Timer_Ticks format to
  29.  *  decrease complexity and overhead in using the format.  First, it can
  30.  *  not be used to represent negative time values.  Second, the routines
  31.  *  are not general. For example, there are no multiply and divide
  32.  *  routines for Timer_Ticks values.  Full generality is obtained by using
  33.  *  the Time module.
  34.  *
  35.  * Copyright 1985, 1988 Regents of the University of California
  36.  * Permission to use, copy, modify, and distribute this
  37.  * software and its documentation for any purpose and without
  38.  * fee is hereby granted, provided that the above copyright
  39.  * notice appear in all copies.  The University of California
  40.  * makes no representations about the suitability of this
  41.  * software for any purpose.  It is provided "as is" without
  42.  * express or implied warranty.
  43.  *
  44.  */
  45.  
  46. #ifndef lint
  47. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/timer/timerClock.c,v 9.7 92/06/01 14:51:55 kupfer Exp $ SPRITE (Berkeley)";
  48. #endif not lint
  49.  
  50. #include <sprite.h>
  51. #include <proc.h>
  52. #include <sync.h>
  53. #include <timer.h>
  54. #include <timerInt.h>
  55. #include <spriteTime.h>
  56. #include <stdlib.h>
  57. #include <string.h>
  58. #include <sys/types.h>
  59. #include <timerTick.h>
  60. #include <machMon.h>
  61.  
  62.  
  63. /*
  64.  *  Universal Time is the number of seconds since 1/1/1970, Greenwich Time.
  65.  *  The time of day is kept in Universal Time. We use the terms "time of
  66.  *  day" and "universal time" interchangably, although the latter is
  67.  *  probably more descriptive. Universal time is mainly used by user-level
  68.  *  programs. "System time" is what the kernel typically uses to measure
  69.  *  time.  System time is the amount of time that the machine has been up.
  70.  *  It is usually maintained by a free-running counter and expressed in
  71.  *  units of ticks. System time is a more useful abstraction since it
  72.  *  is not reset, whereas universal time can be adjusted by a system call.
  73.  *
  74.  *  The term "time" is very overloaded in both the comments and the code.
  75.  *  I've tried to make it as clear as possible which format a particular
  76.  *  time variable is stored in.  Time variables with standard names are
  77.  *  of type "Time".  Time variables with "Tk" appended to their names
  78.  *  are of type "Timer_Ticks".  There may be exceptions to this naming
  79.  *  scheme.
  80.  *
  81.  *  Universal time can be obtained through one of two routines.
  82.  *  Timer_GetRealTimeOfDay is quite accurate but the routine is slow due
  83.  *  to the conversion from internal Timer_Ticks format to Time format.
  84.  *  The other value is timerTimeOfDay and is returned by
  85.  *  Timer_GetTimeOfDay.  The value is updated at every request timer
  86.  *  interrupt by adding the amount of time between interrupts and hence is
  87.  *  very inexpensive to calculate.  Also, the resolution is limited to the
  88.  *  time between interrupts. However, the value is not always accurate
  89.  *  because a timer interrupt can be delayed, therefore it is a close
  90.  *  approximation to the real time of day.  To keep the value roughly
  91.  *  accurate, every 10 seconds timer_UniversalApprox is updated to the real
  92.  *  time of day by a routine called from the timer queue.  The value
  93.  *  of timer_UniversalApprox is guaranteed to be monotonically increasing
  94.  *  between calls to Timer_SetTimeOfDay.
  95.  *
  96.  *  When the universal time is initially set with Timer_SetTimeOfDay, the
  97.  *  current system time is recorded (in systemWhenUniversalSetTk) along with 
  98.  *  the new value for the universal time (in universalWhenSetTk).
  99.  *  This value for universal time is not incremented. When 
  100.  *  Timer_GetTimeOfDay is called, the current universal time is calculated by 
  101.  *  reading the current system time and subtracting from it the system time 
  102.  *  when Timer_SetTimeOfDay was called. This difference is added to the 
  103.  *  recorded universal time to give the current universal time.
  104.  *  
  105.  *  timerUniversalToLocalOffset is used to convert from universal time
  106.  *  to local time. It is the number of minutes to add to universal time
  107.  *  to compute the local time. For example, timerLocalOffset for the 
  108.  *  Pacific time zone is -540 minutes. The local time of day is computed 
  109.  *  by multiplying timerUniversalToLocalOffset by 60 and adding the result 
  110.  *  to the universal time.
  111.  *
  112.  *  timerDSTAllowed is a flag to indicate if Daylight Savings Time is allowed.
  113.  *  A few states, such as Arizona, do not have DST.
  114.  *  (TRUE == DST is allowed, FALSE == DST is not allowed).
  115.  */
  116.  
  117. static Timer_Ticks systemWhenUniversalSetTk;
  118. static Timer_Ticks universalWhenSetTk;
  119.  
  120. Time           timer_UniversalApprox;
  121. int         timerUniversalToLocalOffset;
  122. Boolean     timerDSTAllowed;
  123.  
  124. #ifdef ADJTIME
  125. Time        timer_AdjustDelta;
  126. unsigned    timer_TickAdjust;
  127. int        timer_TickDelta;
  128. #endif
  129.  
  130. /*
  131.  * Semaphore protecting the above time of day variables.
  132.  */
  133.  
  134. Sync_Semaphore    timer_ClockMutex;
  135.  
  136.  
  137. /* 
  138.  * List of "whining" messages that have been displayed recently.  These are 
  139.  * generally error messages that we want to keep from flooding the console.
  140.  */
  141.  
  142. #define WHINE_INTERVAL    30    /* time to allow between messages */
  143.  
  144. typedef struct {
  145.     List_Links links;
  146.     char *message;        /* the message */
  147.     time_t lastDisplayTime;    /* when it was last displayed */
  148. } WhineMsg;
  149.  
  150. static List_Links whineListHdr;
  151. static List_Links *whineList = &whineListHdr;
  152.  
  153. static Sync_Lock whineLock = Sync_LockInitStatic("timer:whineLock");
  154.                 /* lock to protect whineList */
  155.  
  156.  
  157. /*
  158.  * UpdateTimeOfDay() adjusts timerTimeOfDay to the real time of day.
  159.  */
  160.  
  161. static void UpdateUniversalTimeApprox _ARGS_((Timer_Ticks timeTicks, 
  162.                   ClientData  clientData));
  163. static Timer_QueueElement      updateElement;
  164.  
  165. /* (More) forward references */
  166.  
  167. static WhineMsg *NewWhineMsg _ARGS_((char *message));
  168. static void FreeWhineMsg _ARGS_((WhineMsg *msgPtr));
  169.  
  170.  
  171.  
  172. /*
  173.  *----------------------------------------------------------------------
  174.  *
  175.  * TimerClock_Init --
  176.  *
  177.  *    Initializes the data structures necessary to manage the timer
  178.  *    modules' time of day clock.
  179.  *
  180.  * Results:
  181.  *     None.
  182.  *
  183.  * Side effects:
  184.  *     The system counter is initialized and started.
  185.  *
  186.  *----------------------------------------------------------------------
  187.  */
  188.  
  189. void
  190. TimerClock_Init()
  191. {
  192.  
  193.     Time    universal;
  194.     int        offset;
  195.     Boolean    DST;
  196.  
  197.     Sync_SemInitDynamic(&timer_ClockMutex,"Timer:timer_ClockMutex");
  198.  
  199.     Timer_CounterInit();
  200. #ifdef ADJTIME
  201.     timer_AdjustDelta.seconds = timer_AdjustDelta.microseconds = 0;
  202.     timer_TickAdjust = 10;
  203.     timer_TickDelta = 0;
  204. #endif
  205.     universal.seconds = 0;
  206.     universal.microseconds = 0;
  207.     offset = 0;
  208.     DST = TRUE;
  209.     TimerHardwareUniversalTimeInit(&universal, &offset, &DST);
  210.     TimerSetSoftwareUniversalTime(&universal, offset, DST);
  211.     /*
  212.      * Add the routine to fix the time of day to the timer queue.
  213.      * The routine is called every 10 seconds.
  214.      */
  215.  
  216.     updateElement.routine = UpdateUniversalTimeApprox;
  217.     updateElement.interval = 10 * timer_IntOneSecond;
  218.     Timer_ScheduleRoutine(&updateElement, TRUE);
  219.  
  220.     /* 
  221.      * Also initialize the "whine message" list.
  222.      */
  223.     List_Init(whineList);
  224. }
  225.  
  226.  
  227.  
  228. /*
  229.  *----------------------------------------------------------------------
  230.  *
  231.  *  Timer_GetRealTimeOfDay --
  232.  *
  233.  *    Retrieves an accurate value for the time of day. 
  234.  *    This routine is much slower than Timer_GetTimeOfDay but
  235.  *    returns a much more truthful value for the time of day.
  236.  *
  237.  *  Results:
  238.  *    The time of day is returned.
  239.  *
  240.  *  Side Effects:
  241.  *    Updates the global variables that stores the system up-time.
  242.  *
  243.  *----------------------------------------------------------------------
  244.  */
  245.  
  246. void
  247. Timer_GetRealTimeOfDay(timePtr, timerLocalOffsetPtr, DSTPtr)
  248.     Time *timePtr;        /* Buffer to hold TOD. */
  249.     int  *timerLocalOffsetPtr;    /* Optional buffer to hold local offset. */
  250.     Boolean *DSTPtr;        /* Optional buffer to hold DST allowed flag. */
  251. {
  252.     Timer_Ticks    curSystemTk;    /* current system time */
  253.     Timer_Ticks    diffTk;
  254.  
  255.     /*
  256.      *  Get the current system time and subtract from it the system time
  257.      *  when the universal time was last set. Add this difference to the value
  258.      *  of the stored universal time to get the current universal time.
  259.      */
  260.  
  261.     MASTER_LOCK(&timer_ClockMutex);
  262.  
  263.     Timer_GetCurrentTicks(&curSystemTk);
  264.  
  265.     Timer_SubtractTicks(curSystemTk, systemWhenUniversalSetTk, &diffTk);
  266.     Timer_AddTicks(diffTk, universalWhenSetTk, &diffTk);
  267.     Timer_TicksToTime(diffTk, timePtr);
  268.  
  269.     if (timerLocalOffsetPtr != (int *) NIL) {
  270.     *timerLocalOffsetPtr = timerUniversalToLocalOffset;
  271.     }
  272.     if (DSTPtr != (Boolean *) NIL) {
  273.     *DSTPtr = timerDSTAllowed;
  274.     }
  275.     MASTER_UNLOCK(&timer_ClockMutex);
  276.  
  277. }
  278.  
  279.  
  280. /*
  281.  *----------------------------------------------------------------------
  282.  *
  283.  *  Timer_GetRealTimeFromTicks --
  284.  *
  285.  *    Gives an accurate translation of ticks to time.  This routine
  286.  *    returns an absolute time value, rather than the relative time
  287.  *    value since booting returned by Timer_TicksToTime.  This routine
  288.  *    is slower, though.
  289.  *
  290.  *  Results:
  291.  *    The time of the tick value is returned.
  292.  *
  293.  *  Side Effects:
  294.  *    Updates the global variables that stores the system up-time.
  295.  *
  296.  *----------------------------------------------------------------------
  297.  */
  298.  
  299. void
  300. Timer_GetRealTimeFromTicks(ticks, timePtr, timerLocalOffsetPtr, DSTPtr)
  301.     Timer_Ticks    ticks;        /* Ticks value to convert to time. */
  302.     Time *timePtr;        /* Buffer to hold time value. */
  303.     int  *timerLocalOffsetPtr;    /* Optional buffer to hold local offset. */
  304.     Boolean *DSTPtr;        /* Optional buffer to hold DST allowed flag. */
  305. {
  306.     Timer_Ticks    diffTk;
  307.  
  308.     /*
  309.      * No masterlock, since we can be called from a call-back and get deadlock.
  310.      */
  311.  
  312.     /*
  313.      *  Get the tick value and subtract from it the system time
  314.      *  when the universal time was last set. Add this difference to the value
  315.      *  of the stored universal time to get the universal time at that tick
  316.      *  value.
  317.      */
  318.  
  319.     Timer_SubtractTicks(ticks, systemWhenUniversalSetTk, &diffTk);
  320.     Timer_AddTicks(diffTk, universalWhenSetTk, &diffTk);
  321.     Timer_TicksToTime(diffTk, timePtr);
  322.  
  323.     if (timerLocalOffsetPtr != (int *) NIL) {
  324.     *timerLocalOffsetPtr = timerUniversalToLocalOffset;
  325.     }
  326.     if (DSTPtr != (Boolean *) NIL) {
  327.     *DSTPtr = timerDSTAllowed;
  328.     }
  329.  
  330.     return;
  331. }
  332.  
  333.  
  334. /*
  335.  *----------------------------------------------------------------------
  336.  *
  337.  *  Timer_GetTimeOfDay--
  338.  *
  339.  *    Retrieves an approximate universal time. 
  340.  *    The value is approximate because it is updated at every
  341.  *    timer interrupt and timer interrupts may be delayed or dropped.
  342.  *    For an accurate value, use Timer_GetRealTimeOfDay.
  343.  *
  344.  *    Though the time of day value may not be accurate, it is
  345.  *    guaranteed to be monotonically increasing (i.e. it never goes
  346.  *    backwards) between calls to Timer_SetTimeOfDay.
  347.  *
  348.  *  Results:
  349.  *    The approximate time of day is returned.
  350.  *
  351.  *  Side Effects:
  352.  *    None.
  353.  *
  354.  *----------------------------------------------------------------------
  355.  */
  356.  
  357. void
  358. Timer_GetTimeOfDay(timePtr, timerLocalOffsetPtr, DSTPtr)
  359.     Time *timePtr;        /* Buffer to hold TOD. */
  360.     int  *timerLocalOffsetPtr;    /* Optional buffer to hold local offset. */
  361.     Boolean *DSTPtr;        /* Optional buffer to hold DST allowed flag. */
  362. {
  363.  
  364.  
  365.     MASTER_LOCK(&timer_ClockMutex);
  366.  
  367.     *timePtr = timer_UniversalApprox;
  368.  
  369.     if (timerLocalOffsetPtr != (int *) NIL) {
  370.     *timerLocalOffsetPtr = timerUniversalToLocalOffset;
  371.     }
  372.     if (DSTPtr != (Boolean *) NIL) {
  373.     *DSTPtr = timerDSTAllowed;
  374.     }
  375.  
  376.  
  377.     MASTER_UNLOCK(&timer_ClockMutex);
  378.  
  379. }
  380.  
  381.  
  382. /*
  383.  *----------------------------------------------------------------------
  384.  *
  385.  *  Timer_SetTimeOfDay --
  386.  *
  387.  *    Changes the universal time to a new value. This is done in
  388.  *    both the software and hardware clocks (if the machine has one).
  389.  *    IMPORTANT: we don't set the hardware clock because it will won't
  390.  *    work on a ds3100. See the comment in TimerSetHardwareUniversalTime.
  391.  *
  392.  *  Results:
  393.  *    None.
  394.  *
  395.  *  Side Effects:
  396.  *    Universal time is changed.
  397.  *
  398.  *----------------------------------------------------------------------
  399.  */
  400.  
  401. void
  402. Timer_SetTimeOfDay(newUniversal, newLocalOffset, newDSTAllowed)
  403.     Time newUniversal;        /* New value for time of day. */
  404.     int  newLocalOffset;    /* New value for local offset. */
  405.     Boolean newDSTAllowed;    /* New value for DST allowed flag. */
  406. {
  407.     MASTER_LOCK(&timer_ClockMutex);
  408.     TimerSetSoftwareUniversalTime(&newUniversal, 
  409.     newLocalOffset, newDSTAllowed);
  410. #ifdef NOTDEF
  411.     TimerSetHardwareUniversalTime(&newUniversal, newLocalOffset, 
  412.     newDSTAllowed);
  413. #endif
  414.     MASTER_UNLOCK(&timer_ClockMutex);
  415. }
  416.  
  417.  
  418. /*
  419.  *----------------------------------------------------------------------
  420.  *
  421.  *  TimerSetSoftwareUniversalTime --
  422.  *
  423.  *    Changes the universal time to a new value.
  424.  *
  425.  *  Results:
  426.  *    None.
  427.  *
  428.  *  Side Effects:
  429.  *    Updates the global variables that stores the universal time and the 
  430.  *    system time when it was set.
  431.  *
  432.  *----------------------------------------------------------------------
  433.  */
  434.  
  435. void
  436. TimerSetSoftwareUniversalTime(newUniversal, newLocalOffset, newDSTAllowed)
  437.     Time *newUniversal;        /* New value for time of day. */
  438.     int  newLocalOffset;    /* New value for local offset. */
  439.     Boolean newDSTAllowed;    /* New value for DST allowed flag. */
  440. {
  441.  
  442.     /*
  443.      *  Record when the universal time was changed by saving the current 
  444.      *  system time.
  445.      *  Also store the new universal time (it has to be converted to ticks),
  446.      *  the new local offset and the DST flag.
  447.      */
  448.  
  449.  
  450.  
  451.     timer_UniversalApprox = *newUniversal;
  452.  
  453.     Timer_GetCurrentTicks(&systemWhenUniversalSetTk);
  454.     Timer_TimeToTicks(*newUniversal, &universalWhenSetTk);
  455.  
  456.  
  457.     timerUniversalToLocalOffset = newLocalOffset;
  458.     timerDSTAllowed = newDSTAllowed;
  459.  
  460.  
  461. }
  462.  
  463. /*
  464.  *----------------------------------------------------------------------
  465.  *
  466.  * UpdateUniversalTimeApprox --
  467.  *
  468.  *    Called from the timer queue to make timer_UniversalApprox close
  469.  *    to the real current time..
  470.  *
  471.  * Results:
  472.  *    None.
  473.  *
  474.  * Side effects:
  475.  *    timerUTApprox is updated.
  476.  *
  477.  *----------------------------------------------------------------------
  478.  */
  479.  
  480. static void
  481. UpdateUniversalTimeApprox(timeTicks, clientData)
  482.     Timer_Ticks timeTicks;    /* Not used. */
  483.     ClientData    clientData;    /* Not used. */
  484. {
  485.     /* 
  486.      * No need to get the timerClock Mutex lock because 
  487.      * Timer_GetRealTimeOfDay gets it for us.
  488.      */
  489.     Timer_GetRealTimeOfDay(&timer_UniversalApprox, (int *) NIL, (int *) NIL);
  490.     Timer_ScheduleRoutine(&updateElement, TRUE);
  491. }
  492.  
  493. #ifdef ADJTIME
  494. /*
  495.  *----------------------------------------------------------------------
  496.  *
  497.  * Timer_AdjustTime --
  498.  *
  499.  *    Set a new time delta for adjusting the time, and return
  500.  *    the old one.
  501.  *
  502.  * Results:
  503.  *    None.
  504.  *
  505.  * Side effects:
  506.  *    timer_AdjustDelta and timer_TickDelta are updated.
  507.  *
  508.  *----------------------------------------------------------------------
  509.  */
  510. /*ARGSUSED*/
  511. ReturnStatus
  512. Timer_AdjustTime(newDelta, oldDelta)
  513.     Time    *newDelta;
  514.     Time    *oldDelta;
  515. {
  516.     int negative;
  517.  
  518.     MASTER_LOCK(&timer_ClockMutex);
  519.     if (oldDelta != USER_NIL) {
  520.     if (Proc_ByteCopy(FALSE, sizeof(Time),
  521.         (Address) &timer_AdjustDelta, (Address) oldDelta) != SUCCESS) {
  522.         MASTER_UNLOCK(&timer_ClockMutex);
  523.         return SYS_ARG_NOACCESS;
  524.     }
  525.     }
  526.     if (newDelta == USER_NIL) {
  527.     MASTER_UNLOCK(&timer_ClockMutex);
  528.     return oldDelta == USER_NIL ? SYS_ARG_NOACCESS : SUCCESS;
  529.     } else if (Proc_ByteCopy(TRUE, sizeof(Time),
  530.         (Address) newDelta, (Address) &timer_AdjustDelta) != SUCCESS) {
  531.     MASTER_UNLOCK(&timer_ClockMutex);
  532.     return SYS_ARG_NOACCESS;
  533.     }
  534.     /* normalize */
  535.     timer_AdjustDelta.seconds += timer_AdjustDelta.microseconds / ONE_SECOND;
  536.     timer_AdjustDelta.microseconds %= ONE_SECOND;
  537.     negative = timer_AdjustDelta.seconds < 0 ||
  538.     (timer_AdjustDelta.seconds == 0 && timer_AdjustDelta.microseconds < 0);
  539.     if (negative && (timer_AdjustDelta.microseconds > 0)) {
  540.     timer_AdjustDelta.microseconds -= ONE_SECOND;
  541.     ++timer_AdjustDelta.seconds;
  542.     }
  543.     if (!negative && (timer_AdjustDelta.microseconds < 0)) {
  544.     timer_AdjustDelta.microseconds += ONE_SECOND;
  545.     --timer_AdjustDelta.seconds;
  546.     }
  547.     if (negative) {
  548.     timer_TickDelta = -timer_TickAdjust;
  549.     } else {
  550.     timer_TickDelta = timer_TickAdjust;
  551.     }
  552.     MASTER_UNLOCK(&timer_ClockMutex);
  553.     return SUCCESS;
  554. }
  555.  
  556. /*
  557.  *----------------------------------------------------------------------
  558.  *
  559.  * Timer_GetParams --
  560.  *
  561.  *    Return the current tick adjustment.
  562.  *
  563.  * Results:
  564.  *    None.
  565.  *
  566.  * Side effects:
  567.  *    None.
  568.  *
  569.  *----------------------------------------------------------------------
  570.  */
  571. /*ARGSUSED*/
  572. ReturnStatus
  573. Timer_GetParams(tickadj)
  574.     unsigned    *tickadj;
  575. {
  576.     MASTER_LOCK(&timer_ClockMutex);
  577.     if (tickadj == USER_NIL || Proc_ByteCopy(FALSE, sizeof(unsigned),
  578.         (Address) &timer_TickAdjust, (Address) tickadj) != SUCCESS) {
  579.     MASTER_UNLOCK(&timer_ClockMutex);
  580.     return(SYS_ARG_NOACCESS);
  581.     }
  582.     MASTER_UNLOCK(&timer_ClockMutex);
  583.     return SUCCESS;
  584. }
  585.  
  586. /*
  587.  *----------------------------------------------------------------------
  588.  *
  589.  * Timer_SetParams --
  590.  *
  591.  *    Set the tick adjustment.
  592.  *
  593.  * Results:
  594.  *    None.
  595.  *
  596.  * Side effects:
  597.  *    timer_TickAdjust and timer_TickDelta are updated.
  598.  *
  599.  *----------------------------------------------------------------------
  600.  */
  601. /*ARGSUSED*/
  602. ReturnStatus
  603. Timer_SetParams(tickadj)
  604.     unsigned    tickadj;
  605. {
  606.     MASTER_LOCK(&timer_ClockMutex);
  607.     if (tickadj < TIMER_CALLBACK_INTERVAL_APPROX &&
  608.         tickadj > 0 && (ONE_SECOND % tickadj) == 0) {
  609.     timer_TickAdjust = tickadj;
  610.     if (timer_AdjustDelta.seconds < 0
  611.         || (timer_AdjustDelta.seconds == 0
  612.         && timer_AdjustDelta.microseconds < 0)) {
  613.         timer_TickDelta = -timer_TickAdjust;
  614.     } else {
  615.         timer_TickDelta = timer_TickAdjust;
  616.     }
  617.     MASTER_UNLOCK(&timer_ClockMutex);
  618.     return SUCCESS;
  619.     } else {
  620.     MASTER_UNLOCK(&timer_ClockMutex);
  621.     return GEN_INVALID_ARG;
  622.     }
  623. }
  624. #endif
  625.  
  626.  
  627. /*
  628.  *----------------------------------------------------------------------
  629.  *
  630.  * Timer_OkToWhine --
  631.  *
  632.  *    Keep track of when a given message was last displayed, and tell 
  633.  *    whether it's too soon to display it again.
  634.  *
  635.  * Results:
  636.  *    Returns TRUE if it's been more than WHINE_INTERVAL seconds since
  637.  *    the last time the given message had been displayed.
  638.  *
  639.  * Side effects:
  640.  *    None.
  641.  *
  642.  *----------------------------------------------------------------------
  643.  */
  644.  
  645. Boolean
  646. Timer_OkToWhine(message)
  647.     char *message;        /* message to check */
  648. {
  649.     WhineMsg *msgPtr;        /* a message in the list */
  650.     WhineMsg *delPtr;        /* message to remove from the list */
  651.     Boolean okay = TRUE;    /* okay to display the message? */
  652.     Boolean inList = FALSE;    /* is the message already in the list? */
  653.     time_t now = Timer_GetUniversalTimeInSeconds();
  654.  
  655.     Sync_GetLock(&whineLock);
  656.  
  657.     /* 
  658.      * If the message was displayed within the last WHINE_INTERVAL seconds, 
  659.      * don't display it again.  Otherwise, note the new display time.
  660.      */
  661.     LIST_FORALL(whineList, (List_Links *)msgPtr) {
  662.     if (strcmp(msgPtr->message, message) == 0) {
  663.         if (now >= msgPtr->lastDisplayTime + WHINE_INTERVAL) {
  664.         msgPtr->lastDisplayTime = now;
  665.         } else {
  666.         okay = FALSE;
  667.         }
  668.         inList = TRUE;
  669.         break;
  670.     } 
  671.     }
  672.  
  673.     /* 
  674.      * If the message isn't in the list, add it.
  675.      */
  676.     if (!inList) {
  677.     msgPtr = NewWhineMsg(message);
  678.     List_Insert((List_Links *)msgPtr, LIST_ATREAR(whineList));
  679.     }
  680.  
  681.     /* 
  682.      * Garbage collect any old messages.
  683.      */
  684.     msgPtr = (WhineMsg *)List_First(whineList);
  685.     while (!List_IsAtEnd(whineList, (List_Links *)msgPtr)) {
  686.     if (now < msgPtr->lastDisplayTime + WHINE_INTERVAL) {
  687.         msgPtr = (WhineMsg *)List_Next((List_Links *)msgPtr);
  688.     } else {
  689.         delPtr = msgPtr;
  690.         msgPtr = (WhineMsg *)List_Next((List_Links *)msgPtr);
  691.         List_Remove((List_Links *)delPtr);
  692.         FreeWhineMsg(delPtr);
  693.     }
  694.     }
  695.  
  696.     Sync_Unlock(&whineLock);
  697.     return okay;
  698. }
  699.  
  700.  
  701. /*
  702.  *----------------------------------------------------------------------
  703.  *
  704.  * NewWhineMsg --
  705.  *
  706.  *    Create and initialize a new WhineMsg.
  707.  *
  708.  * Results:
  709.  *    Returns an initialized WhineMsg.
  710.  *
  711.  * Side effects:
  712.  *    None.
  713.  *
  714.  *----------------------------------------------------------------------
  715.  */
  716.  
  717. static WhineMsg *
  718. NewWhineMsg(message)
  719.     char *message;        /* the text of the message to record */
  720. {
  721.     WhineMsg *msgPtr;
  722.  
  723.     msgPtr = (WhineMsg *)malloc(sizeof(WhineMsg));
  724.     List_InitElement((List_Links *)msgPtr);
  725.     msgPtr->message = strdup(message);
  726.     msgPtr->lastDisplayTime = Timer_GetUniversalTimeInSeconds();
  727.  
  728.     return msgPtr;
  729. }
  730.  
  731.  
  732. /*
  733.  *----------------------------------------------------------------------
  734.  *
  735.  * FreeWhineMsg --
  736.  *
  737.  *    Destroy a WhineMsg.
  738.  *
  739.  * Results:
  740.  *    None.
  741.  *
  742.  * Side effects:
  743.  *    None.
  744.  *
  745.  *----------------------------------------------------------------------
  746.  */
  747.  
  748. static void
  749. FreeWhineMsg(msgPtr)
  750.     WhineMsg *msgPtr;        /* the message to free */
  751. {
  752.     free((Address)msgPtr->message);
  753.     free((Address)msgPtr);
  754. }
  755.